package com.example.drive_safe;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.CalendarContract;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;

@SuppressLint("NewApi")
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class SMSManager extends BroadcastReceiver implements GPSCallback {

	private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
	private static final String APPLICATION_ON = "com.drivesafe.APPLICATION_ON";
	private static final String APPLICATION_OFF = "com.drivesafe.APPLICATION_OFF";
	private static GPSManager gpsManager = null;
	private static double speed = 0.0;
	private static int textCount = 0;
	public static Context context = null;
	private static boolean isRunning = false;
	private static SharedPreferences sharedPrefs = null;
	private static String method = null;


	private static List<String> ex_list = new ArrayList<String>();

	// This method returns true if the incoming number matches with exclusion list numbers
	public boolean checkList(String from) {
		from = formatPhoneNumber(from);
//		SharedPreferences sharedPrefs = PreferenceManager
//				.getDefaultSharedPreferences(context);
//		ex_list.add(sharedPrefs.getString("list1", "none"));
//		ex_list.add(sharedPrefs.getString("list2", "none"));
//		ex_list.add(sharedPrefs.getString("list3", "none"));
		for (int i = 0; i <ex_list.size(); i++) {
			Log.d("excl", ex_list.get(i));
			if (!ex_list.get(i).equals("none") && ex_list.get(i).equals(from)) {
				return true;
			}
		}
		return false;
	}
	
	
	// Format string: exclude all non-numeric characters
	private static String formatPhoneNumber(String number) {
		number = number.replaceAll("[^\\d]", "");
		return number;
	}
	
	// Parse the contact string and return an array of numbers
	private static String[] readAndParse(String pass) {		
		String contactNumbers[] = null;
		String [] nameAndContact =pass.split(":");
				
		if(nameAndContact.length > 1) {	//	the contact string has a set of numbers
			contactNumbers = nameAndContact[1].split("/");
		}
		
		return contactNumbers;
	}
	
	
	// Populate the exclusion list for this class
	private void populateExclusionList() {
		
		// Read the exclusion list from shared preferences
		Set<String> exclusionContacts = SMSManager.sharedPrefs.getStringSet("exclContacts", null);
		
		if(exclusionContacts == null)
			return;
		
		String[] contactNumbers;

		for(String pass: exclusionContacts) {
			
			// Parse the numbers from the current string
			contactNumbers = readAndParse(pass);
			
			if(contactNumbers != null) {				
				for(String contactNumber: contactNumbers) {
					
					// Format phone numbers and add to the exclusion list
					contactNumber = formatPhoneNumber(contactNumber);
					if( !ex_list.contains(contactNumber) )
						ex_list.add(contactNumber);
				}
			}
		}
		
		return;
	}

	
	@Override
	// Different actions depending on the state of the application (ON/OFF/Running) when a sms is received
	public void onReceive(Context context, Intent intent) {
		if (intent.getAction().equals(SMSManager.APPLICATION_ON)) {
			SMSManager.context = context;
			startApplication();
		}
		if (intent.getAction().equals(SMSManager.APPLICATION_OFF)) {
			stopApplication();
		}
		if (intent.getAction().equals(SMSManager.SMS_RECEIVED) && isRunning) {
			handleNewText(intent);
		}
		
		// Logging result for handling new text
		switch (getResultCode()) {
		case Activity.RESULT_OK:
			Log.d("SMS Sent", "SMS Sent");
			break;
		case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
			Log.d("Generic failure", "Generic failure");
			break;
		case SmsManager.RESULT_ERROR_NO_SERVICE:
			Log.d("No service", "No service");
			break;
		}
	}

	
	// Start the application and initialize data
	private void startApplication() {
		
		SMSManager.isRunning = true;
		SMSManager.sharedPrefs = PreferenceManager
				.getDefaultSharedPreferences(context);
	
		populateExclusionList();
		
		SMSManager.method = sharedPrefs.getString("prefMethod", "GPS");
		if (SMSManager.method.equals("GPS") || SMSManager.method.equals("Both")) {
			if (SMSManager.gpsManager == null) {
				SMSManager.gpsManager = new GPSManager();
				SMSManager.gpsManager.setGPSCallback(this);
			}

			// Switch between test and production
			// SMSManager.gpsManager.startListner(context);
			SMSManager.gpsManager.startTesting(context);
		}
	}

	@SuppressLint("NewApi")
	// Stop the application and set data
	private void stopApplication() {
		SMSManager.isRunning = false;
		try {
			if (SMSManager.gpsManager != null) {
				SMSManager.gpsManager.stopListner();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		resumeNotification();
	}
	
	
	@SuppressLint("NewApi")
	// Notify the user about a blocked message when the vehicle is not in speed 
	private void resumeNotification() {
		if (SMSManager.textCount > 0) {	// If at least one text is received while driving 
			PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
					new Intent(context, SMSManager.class), 0);
			
			// Create the notifications
			Notification notification = new Notification.Builder(context)
					.setContentTitle("New text message")
					.setContentText(
							"You have got " + textCount
									+ " new texts while driving")
					.setSmallIcon(R.drawable.ic_launcher)
					.setContentIntent(contentIntent).build();
			notification.flags |= Notification.FLAG_AUTO_CANCEL;

			NotificationManager notificationManager = (NotificationManager) context
					.getSystemService(Context.NOTIFICATION_SERVICE);
			
			// Notify the user
			notificationManager.notify(0, notification);
		}
		SMSManager.textCount = 0;
	}
	
	
	@SuppressLint("NewApi")
	// Check the calendar for "Driving" event (Scheduled)
	public static boolean checkScheduler() {
		
			String[] proj = 
			      new String[]{
						CalendarContract.Events._ID, 
						CalendarContract.Events.DTSTART, 
						CalendarContract.Events.DTEND, 
						CalendarContract.Events.TITLE};
			
			Cursor cursor = 
			      context.getContentResolver().query(CalendarContract.Events.CONTENT_URI, proj, 
			              CalendarContract.Events.DTSTART + " <= " + (new Date()).getTime() +
			             " AND " + CalendarContract.Events.ALL_DAY + " = " + 0  +
			              " AND " + CalendarContract.Events.DTEND + " >= " + (new Date()).getTime() +
			               " AND " + CalendarContract.Events.TITLE +" = " + "?",
			             new String[]{ "Driving"}, CalendarContract.Events.DTSTART);
			if (cursor.getCount() > 0) {
			  Log.d("Scheduler", "Event: Driving");
			  return true;
			}
			else{
				Log.d("Scheduler", "No Event");

				return false; 
			}		     
	}

	
	// When a new text is received and the application is running
	private void handleNewText(Intent intent) {
		boolean block = false;
		
		// Get the user's preferred method (GPS or Scheduler or both)
		SMSManager.method = sharedPrefs.getString("prefMethod", "GPS");
		Log.d("SMSManager.method", SMSManager.method);
		
		// GPS
		if (SMSManager.method.equals("GPS")) {
			// block sms only if the user's speed is greater than thresHold speed
			if (getThresholdSpeed() <= SMSManager.speed)	
				block = true;
			Log.d("BlockGPS", block?"true":"false");
		}
		
		// Scheduler
		else if (SMSManager.method.equals("Scheduler")) {
			block = checkScheduler();
			Log.d("BlockScheduler", block?"true":"false");
		}
		
		// Both
		else if (SMSManager.method.equals("Both")) {
			if (getThresholdSpeed() <= SMSManager.speed || checkScheduler())
				block = true;
			Log.d("BlockBoth", block?"true":"false");
		}
		
		// Retrieve the SMS data if first 2 tests pass
		// (i.e. current speed is less than or equal to the threshold value and no driving event is set)
		SmsMessage smsMessage[] = null;
		if(block) {
			Bundle bundle = intent.getExtras();
			Object messages[] = (Object[]) bundle.get("pdus");
			smsMessage = new SmsMessage[messages.length];
			for (int n = 0; n < messages.length; n++) {
				smsMessage[n] = SmsMessage.createFromPdu((byte[]) messages[n]);
			}
		}
		
		
		// If the user prefers to use exclusion list
		if (block && SMSManager.sharedPrefs.getBoolean("prefExclUse", false)) {

			// Iterate all the messages to check if the sender is in the exlusion list
			for (int n = 0; n < smsMessage.length; n++) {
				if (inExclusionList(smsMessage[n].getOriginatingAddress())) {
					block = false;
					break;
				}
			}
		}
		
		Log.d("Block", block?"true":"false");
		
		if (block) {
			SMSManager.textCount++;
			abortBroadcast();
			if (smsMessage != null) {
				restoreSMS(smsMessage);
				if (SMSManager.sharedPrefs.getBoolean("prefAutoReply", false)) {
					sendAutoReply(smsMessage);
				}
			}
			// check for auto reply an send if its true
//			if (SMSManager.sharedPrefs.getBoolean("prefAutoReply", false)) {
//				sendAutoReply(intent);
//			}
		}
		
	}



	// Send an auto-reply to the sms sender while user is driving 
	private void sendAutoReply(Intent intent) {
		Bundle bundle = intent.getExtras();
		SmsMessage[] msgs = null;
		String msg_from;
		String body;
		if (bundle != null) {
			try {
				Object[] pdus = (Object[]) bundle.get("pdus");
				Log.d("Bundle", pdus.toString());
				msgs = new SmsMessage[pdus.length];
				for (int i = 0; i < msgs.length; i++) {
					msgs[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
					msg_from = msgs[i].getOriginatingAddress();
					body = "Busy right now";
					Log.d("msg_from", msg_from);
					Log.d("body", body);
					SmsManager smsManager = SmsManager.getDefault();
					smsManager
							.sendTextMessage(msg_from, null, body, null, null);

				}
			} catch (Exception e) {
				Log.d("Exception caught", e.getMessage());
			}
		}
	}
	
	
	// Send the auto-reply messages to the sms sender while the user is driving
	private void sendAutoReply(SmsMessage[] msgs) {
		String msg_from;
		String body;
		for (SmsMessage msg : msgs) {
			msg_from = msg.getOriginatingAddress();
			body = SMSManager.sharedPrefs.getString("prefAutoReplyCustom", "Busy right now");
			Log.d("msg_from", msg_from);
			Log.d("body", body);
			try{
			SmsManager smsManager = SmsManager.getDefault();
			smsManager
					.sendTextMessage(msg_from, null, body, null, null);
			Toast.makeText(context, "Auto-Reply Sent!",Toast.LENGTH_LONG).show();
			}catch(Exception e){
				Toast.makeText(context, "Auto-Reply Failed!",Toast.LENGTH_LONG).show();
				e.printStackTrace();
			}
		}
	}

	
	// Reading the current threshold value set by the user
	private double getThresholdSpeed() {
		double tspeed = (double) Integer.parseInt(SMSManager.sharedPrefs
				.getString("prefSpeedThreshold", "5"));
		double unitMultiplier = SMSManager.sharedPrefs.getString(
				"prefSpeedUnit", "mile").equals("mile") ? 0.000621371192
				: 0.001;
		return tspeed * 3600 * unitMultiplier;
	}

	@Override
	// checking the vehicle speed against the user defined threshold Speed on GPS update
	// If the current speed is less than the thresHold, it will notify the user about the received sms
	public void onGPSUpdate(Location location) {
		SMSManager.speed = location.getSpeed();
		if(SMSManager.speed < getThresholdSpeed()) {
			resumeNotification();
		}
		Log.d("getSpeed", SMSManager.speed + "");
	}
    
	// Check whether the sender is in the exclusion list
	private boolean inExclusionList(String address) {
		return checkList(address);
	}

	// Saves the blocked sms (along with the sender's phone number) into the users' inbox
	private void restoreSMS(SmsMessage[] msgs) {
		for (SmsMessage msg : msgs) {
			ContentValues values = new ContentValues();
			values.put("address", msg.getOriginatingAddress());
			values.put("body", msg.getMessageBody());
			// values.put("date", msg.getTimestampMillis());
			SMSManager.context.getContentResolver().insert(
					Uri.parse("content://sms/inbox"), values);
		}
	}
	
	
}
